package edu.uoc.tds.server;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

import edu.uoc.tds.bean.CentroDocente;
import edu.uoc.tds.bean.Evento;
import edu.uoc.tds.bean.TipoEvento;
import edu.uoc.tds.bean.Universidad;
import edu.uoc.tds.common.EGestioEventsException;
import edu.uoc.tds.common.Utils;
import edu.uoc.tds.common.Variables;

public class GestorEvento {
	private Connection conn = null;

	public GestorEvento(Connection conn) {
		this.conn = conn;
	}

    public Integer insertarEvento(Evento evento) throws SQLException, EGestioEventsException {
    	PreparedStatement ps = null;
    	ResultSet rs = null;
    	Integer idEvento = null;

    	try {
    		if (validarAltaEvento(evento)) {
	    		String sql = "select nextval('seq_evento')";
	    		ps = conn.prepareStatement(sql);
	    		rs = ps.executeQuery();
	    		if (rs.next()) {
	    			idEvento = new Integer(rs.getInt(1));
	    		}

	    		ps.close();

	    		sql = "INSERT INTO evento (id_evento, id_centro, id_universidad, nombre, " +
	    			"descripcion, fecha_inicio, fecha_fin, fecha_alta, estado, " +
	    			"id_tipo_evento, plazas, umbral, duracion, precio) " +
	    			"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
	    		ps = conn.prepareStatement(sql);
	    		ps.setInt(1, idEvento.intValue());
	    		ps.setInt(2, evento.getCentroDocente().getIdCentroDocente().intValue());
	    		ps.setInt(3, evento.getUniversidad().getIdUniversidad().intValue());
	    		ps.setString(4, evento.getNombre());
	    		ps.setString(5, evento.getDescripcion());
	    		ps.setDate(6, new java.sql.Date(evento.getFechaInicio().getTime()));
	    		ps.setDate(7, new java.sql.Date(evento.getFechaFin().getTime()));
	    		ps.setDate(8, new java.sql.Date(evento.getFechaAlta().getTime()));
	    		ps.setInt(9, evento.getEstado().intValue());
	    		ps.setInt(10, evento.getTipoEvento().getId_evento().intValue());
	    		ps.setInt(11, evento.getCapacidad().intValue());
	    		ps.setInt(12, evento.getUmbral().intValue());
	    		ps.setInt(13, evento.getDuracion().intValue());
	    		ps.setDouble(14, evento.getPrecio().doubleValue());
	    		ps.executeUpdate();

	    		conn.commit();
    		}
    	} catch (SQLException e) {
    		conn.rollback();
    		throw e;
    	} finally {
    		if (rs != null) {
    			rs.close();
    		}
    		if (ps != null) {
    			ps.close();
    		}
    	}

    	return idEvento;
    }

    private boolean validarAltaEvento(Evento evento) throws EGestioEventsException, SQLException {
		if (validarDatosObligatorios(evento)) {
			if (existeSolapamiento(evento)) {
            	EGestioEventsException e = new EGestioEventsException();
        		e.setDescripcion("pantGestionEventos.err.alta.solapamientodesc");
        		e.setTitulo("pantGestionEventos.err.titulo.alta");
        		throw e;
			}
		}
    	return true;
    }

    public void modificarEvento(Evento evento) throws SQLException, EGestioEventsException {
    	PreparedStatement ps = null;

    	try {
    		if (validarModificacion(evento)) {
	    		String sql = "UPDATE evento SET nombre = ?, descripcion = ?, fecha_inicio = ?, " +
	    			"fecha_fin = ?, estado = ?, fecha_estado = ?, motivo_estado = ?, " +
	    			"id_tipo_evento = ?, plazas = ?, duracion = ?, precio = ? " +
	    			"WHERE id_evento = ?";
	    		ps = conn.prepareStatement(sql);
	    		ps.setString(1, evento.getNombre());
	    		ps.setString(2, evento.getDescripcion());
	    		ps.setDate(3, new java.sql.Date(evento.getFechaInicio().getTime()));
	    		ps.setDate(4, new java.sql.Date(evento.getFechaFin().getTime()));
	    		ps.setInt(5, evento.getEstado().intValue());
	    		if (evento.getFechaEstado() != null) {
	    			ps.setDate(6, new java.sql.Date(evento.getFechaEstado().getTime()));
	    		} else {
	    			ps.setNull(6, Types.DATE);
	    		}
	    		if (evento.getMotivoEstado() != null) {
	    			ps.setString(7, evento.getMotivoEstado());
	    		} else {
	    			ps.setNull(7, Types.VARCHAR);
	    		}
	    		ps.setInt(8, evento.getTipoEvento().getId_evento().intValue());
	    		ps.setInt(9, evento.getCapacidad().intValue());
	    		ps.setInt(10, evento.getDuracion().intValue());
	    		ps.setDouble(11, evento.getPrecio().doubleValue());
	    		ps.setInt(12, evento.getIdEvento().intValue());
	    		ps.executeUpdate();

	    		conn.commit();
    		}
    	} catch (SQLException e) {
    		conn.rollback();
    		throw e;
    	} finally {
    		if (ps != null) {
    			ps.close();
    		}
    	}
    }

    private boolean validarModificacion(Evento evento) throws SQLException, EGestioEventsException {
    	if (validarDatosObligatorios(evento)) {
	    	if (!eventoComenzado(evento.getIdEvento())) {
	    		if (!umbralAlcanzado(evento)) {
	    			if (existeSolapamiento(evento)) {
		            	EGestioEventsException e = new EGestioEventsException();
		        		e.setDescripcion("pantGestionEventos.err.modif.solapamientodesc");
		        		e.setTitulo("pantGestionEventos.err.titulo.modif");
		        		throw e;
	    			}
	    		} else {
	            	EGestioEventsException e = new EGestioEventsException();
	        		e.setDescripcion("pantGestionEventos.err.modif.umbraldesc");
	        		e.setTitulo("pantGestionEventos.err.titulo.modif");
	        		throw e;
	    		}
	    	} else {
	        	EGestioEventsException e = new EGestioEventsException();
	    		e.setDescripcion("pantGestionEventos.err.modif.comenzadodesc");
	    		e.setTitulo("pantGestionEventos.err.titulo.modif");
	    		throw e;
	    	}
    	}
    	return true;
    }

    private boolean umbralAlcanzado(Evento evento) throws SQLException {
    	boolean resultado = false;
    	PreparedStatement ps = null;
    	ResultSet rs = null;

    	try {
    		String sql = "SELECT e.umbral as umbral, e.fecha_inicio as fecha_inicio, " +
    			"e.fecha_fin as fecha_fin, e.plazas as plazas, count(*) as asistencia " +
    			"FROM evento e, inscripcion i " +
    			"WHERE e.id_evento = i.id_evento AND e.id_evento = ? " +
    			"GROUP BY e.umbral, e.fecha_inicio, e.fecha_fin, e.plazas";
    		ps = conn.prepareStatement(sql);
    		ps.setInt(1, evento.getIdEvento().intValue());
    		rs = ps.executeQuery();
    		if (rs.next()) {
    			int umbral = rs.getInt("umbral");
    			int asistencia = rs.getInt("asistencia");
    			int plazas = rs.getInt("plazas");
    			if (umbral < (asistencia / plazas * 100)) {
    				java.util.Date fechaInicio = (java.util.Date) rs.getDate("fecha_inicio");
    				java.util.Date fechaFin = (java.util.Date) rs.getDate("fecha_fin");
    				resultado = !fechaInicio.equals(evento.getFechaInicio()) || !fechaFin.equals(evento.getFechaFin());
    			}
    		}
    	} finally {
    		if (rs != null) {
    			rs.close();
    		}
    		if (ps != null) {
    			ps.close();
    		}
    	}
    	return resultado;
    }

    private boolean validarDatosObligatorios(Evento evento) throws EGestioEventsException {
    	String campoObligatorio = null;

    	if (Utils.isEmpty(evento.getNombre())) {
    		campoObligatorio = "pantGestionEventos.err.nombre.obligatorio";
    	} else if (Utils.isEmpty(evento.getFechaInicio())) {
    		campoObligatorio = "pantGestionEventos.err.fechaini.obligatorio";
    	} else if (Utils.isEmpty(evento.getFechaFin())) {
    		campoObligatorio = "pantGestionEventos.err.fechafin.obligatorio";
    	} else if (Utils.isEmpty(evento.getUmbral())) {
    		campoObligatorio = "pantGestionEventos.err.umbral.obligatorio";
    	} else if (Utils.isEmpty(evento.getPrecio())) {
    		campoObligatorio = "pantGestionEventos.err.precio.obligatorio";
    	} else if (Utils.isEmpty(evento.getCapacidad())) {
    		campoObligatorio = "pantGestionEventos.err.plazas.obligatorio";
    	} else if (Utils.isEmpty(evento.getDuracion())) {
    		campoObligatorio = "pantGestionEventos.err.duracion.obligatorio";
    	} else if (Utils.isEmpty(evento.getDescripcion())) {
    		campoObligatorio = "pantGestionEventos.err.descripcion.obligatorio";
    	} else if (evento.getFechaInicio().getTime()>evento.getFechaFin().getTime()) {
    		campoObligatorio = "pantGestionEventos.err.fechaini.fechafin";
    	}

    	if (campoObligatorio != null) {
        	EGestioEventsException e = new EGestioEventsException();
    		e.setDescripcion(campoObligatorio);
    		e.setTitulo("pantGestionEventos.err.titulo.campos.obligatorios");
    		throw e;
    	}

    	return true;
    }

    public Evento getEvento(Integer idEvento) throws SQLException {
    	Evento evento = null;
    	PreparedStatement ps = null;
    	ResultSet rs = null;

    	try {
    		String sql = "SELECT e.id_centro, e.id_universidad, e.nombre, " +
    			"e.descripcion, e.fecha_inicio, e.fecha_fin, e.fecha_alta, e.estado, " +
    			"e.fecha_estado, e.motivo_estado, e.id_tipo_evento, e.plazas, " +
    			"e.umbral, e.duracion, e.precio, te.descripcion as desc_tipo_evento " +
    			"FROM evento e, tipoevento te " +
    			"WHERE e.id_evento = ? AND e.id_tipo_evento = te.id_tipo_evento";
    		ps = conn.prepareStatement(sql);
    		ps.setInt(1, idEvento.intValue());
    		rs = ps.executeQuery();
    		if (rs.next()) {
    			evento = new Evento(idEvento);
    			evento.setCentroDocente(new CentroDocente(new Integer(rs.getInt("id_centro"))));
    			evento.setUniversidad(new Universidad(new Integer(rs.getInt("id_universidad"))));
    			evento.setNombre(rs.getString("nombre"));
    			evento.setDescripcion(rs.getString("descripcion"));
    			evento.setFechaInicio((java.util.Date) rs.getDate("fecha_inicio"));
    			evento.setFechaFin((java.util.Date) rs.getDate("fecha_fin"));
    			evento.setFechaAlta((java.util.Date) rs.getDate("fecha_alta"));
    			evento.setEstado(new Integer(rs.getInt("estado")));
    			evento.setFechaEstado((java.util.Date) rs.getDate("fecha_estado"));
    			evento.setMotivoEstado(rs.getString("motivo_estado"));
    			evento.setTipoEvento(new TipoEvento(new Integer(rs.getInt("id_tipo_evento")), rs.getString("desc_tipo_evento")));
    			evento.setCapacidad(new Integer(rs.getInt("plazas")));
    			evento.setUmbral(new Integer(rs.getInt("umbral")));
    			evento.setDuracion(new Integer(rs.getInt("duracion")));
    			evento.setPrecio(new Double(rs.getDouble("precio")));
    		}
    	} finally {
    		if (rs != null) {
    			rs.close();
    		}
    		if (ps != null) {
    			ps.close();
    		}
    	}

    	return evento;
    }

    public List<Object[]> consultarEventos(Evento evento, boolean cancelados, boolean finalizados) throws SQLException {
		List<Object[]> lista = new ArrayList<Object[]>();
    	PreparedStatement ps = null;
    	ResultSet rs = null;

    	try {
    		String sql = "SELECT e.fecha_inicio as fecha_inicio, e.nombre as nombre_evento, " +
    			"e.duracion as duracion, c.nombre as nombre_centro, " +
    			"e.id_evento as id_evento, c.id_centro as id_centro " +
    			"FROM evento e, centrodocente c " +
    			"WHERE e.id_centro = c.id_centro ";

                if (evento.getUniversidad().getIdUniversidad() != 0) {
                    sql +=" and e.id_universidad = " + evento.getUniversidad().getIdUniversidad();
                }

    		if (evento.getCentroDocente() != null) {
    			sql += " and e.id_centro = ?";
    		}
    		if (evento.getFechaInicio() != null) {
    			sql += " and e.fecha_inicio >= ?";
    		}
    		if (evento.getFechaFin() != null) {
    			sql += " and e.fecha_fin <= ?";
    		}
    		if (!cancelados) {
    			sql += " and e.estado = ?";
    		}
    		if (!finalizados) {
    			sql += " and e.fecha_fin > ?";
    		}
    		sql += " order by fecha_inicio";
    		ps = conn.prepareStatement(sql);
    		int i = 0;
    		//ps.setInt(i, evento.getUniversidad().getIdUniversidad().intValue());
    		if (evento.getCentroDocente() != null) {
        		ps.setInt(++i, evento.getCentroDocente().getIdCentroDocente().intValue());
    		}
    		if (evento.getFechaInicio() != null) {
        		ps.setDate(++i, new java.sql.Date(evento.getFechaInicio().getTime()));
    		}
    		if (evento.getFechaFin() != null) {
        		ps.setDate(++i, new java.sql.Date(evento.getFechaFin().getTime()));
    		}
    		if (!cancelados) {
        		ps.setInt(++i, Variables.estadoActivo);
    		}
    		if (!finalizados) {
        		ps.setDate(++i, new java.sql.Date(new java.util.Date().getTime()));
    		}
    		rs = ps.executeQuery();

    		Object[] fila = null;
    		while (rs.next()) {
    			fila = new Object[6];
    			fila[0] = ((java.util.Date) rs.getDate("fecha_inicio"));
    			fila[1] = rs.getString("nombre_evento");
    			fila[2] = rs.getString("nombre_centro");
    			fila[3] = new Integer(rs.getInt("duracion"));
    			fila[4] = new Integer(rs.getInt("id_evento"));
    			fila[5] = new Integer(rs.getInt("id_centro"));
    			lista.add(fila);
    		}
       	} finally {
    		if (rs != null) {
    			rs.close();
    		}
    		if (ps != null) {
    			ps.close();
    		}
    	}

    	return lista;
    }

    public void eliminarEvento(Integer idEvento) throws SQLException, EGestioEventsException {
    	PreparedStatement ps = null;

    	try {
    		if (!eventoComenzado(idEvento)) {
    			String sql = "UPDATE evento SET estado = ?, fecha_estado = ? WHERE id_evento = ?";
    			ps = conn.prepareStatement(sql);
    			ps.setInt(1, Variables.estadoInactivo);
    			ps.setDate(2, new java.sql.Date(new java.util.Date().getTime()));
    			ps.setInt(3, idEvento.intValue());
    			ps.executeUpdate();
    			conn.commit();
    		} else {
            	EGestioEventsException e = new EGestioEventsException();
        		e.setDescripcion("pantGestionEventos.err.baja.desc");
        		e.setTitulo("pantGestionEventos.err.titulo.baja");
        		throw e;
    		}
    	} catch (SQLException e) {
    		conn.rollback();
    		throw e;
    	} finally {
    		if (ps != null) {
    			ps.close();
    		}
    	}
    }

    private boolean eventoComenzado(Integer idEvento) throws SQLException {
    	boolean resultado = false;
    	PreparedStatement ps = null;
    	ResultSet rs = null;

    	try {
    		String sql = "SELECT fecha_inicio FROM evento WHERE id_evento = ?";
    		ps= conn.prepareStatement(sql);
    		ps.setInt(1, idEvento.intValue());
    		rs = ps.executeQuery();
    		if (rs.next()) {
    			java.util.Date fechaInicio = (java.util.Date) rs.getDate("fecha_inicio");
    			resultado = new java.util.Date().getTime() > fechaInicio.getTime();
    		}
    	} finally {
    		if (ps != null) {
    			ps.close();
    		}
    		if (rs != null) {
    			rs.close();
    		}
    	}

    	return resultado;
    }

    private boolean existeSolapamiento(Evento evento) throws SQLException {
    	boolean resultado = false;
    	PreparedStatement ps = null;
    	ResultSet rs = null;

    	try {
    		String sql = "SELECT count(*) FROM evento WHERE id_centro = ? AND estado = ? " +
    			"AND ((fecha_inicio <= ? AND fecha_fin >= ?) OR (fecha_inicio <= ? AND fecha_fin >= ?) " +
    			"OR (fecha_inicio >= ? AND fecha_fin <= ?))";
    		if (evento.getIdEvento() != null) {
    			sql += " AND id_evento <> ?";
    		}
    		ps = conn.prepareStatement(sql);
    		java.sql.Date fechaInicio = new java.sql.Date(evento.getFechaInicio().getTime());
    		java.sql.Date fechaFin = new java.sql.Date(evento.getFechaFin().getTime());
    		ps.setInt(1, evento.getCentroDocente().getIdCentroDocente().intValue());
    		ps.setInt(2, Variables.estadoActivo);
    		ps.setDate(3, fechaInicio);
    		ps.setDate(4, fechaInicio);
    		ps.setDate(5, fechaFin);
    		ps.setDate(6, fechaFin);
    		ps.setDate(7, fechaInicio);
    		ps.setDate(8, fechaFin);
    		if (evento.getIdEvento() != null) {
        		ps.setInt(9, evento.getIdEvento().intValue());
    		}
    		rs = ps.executeQuery();
    		if (rs.next()) {
    			resultado = rs.getInt(1) > 0;
    		}
    	} finally {
    		if (ps != null) {
    			ps.close();
    		}
    		if (rs != null) {
    			rs.close();
    		}
    	}

    	return resultado;
    }

    public void modificarPrerequisitos(Evento evento) throws SQLException {
    	PreparedStatement ps = null;

    	try {
    		String sql = "DELETE FROM prerequisitos WHERE id_evento = ?";
    		ps = conn.prepareStatement(sql);
    		ps.setInt(1, evento.getIdEvento().intValue());
    		ps.executeUpdate();

    		ps.close();

    		sql = "INSERT INTO prerequisitos (id_evento, id_eventoprerequisito) VALUES (?, ?)";
    		Evento[] eventosPrereq = evento.getEventosPrereq();
    		if (eventosPrereq != null) {
    			for (int i = 0; i < eventosPrereq.length; i++) {
    				ps = conn.prepareStatement(sql);
    				ps.setInt(1, evento.getIdEvento().intValue());
    				ps.setInt(2, eventosPrereq[i].getIdEvento().intValue());
    				ps.executeUpdate();
    				ps.close();
    			}
    		}

    		conn.commit();
    	} catch (SQLException e) {
    		conn.rollback();
    		throw e;
       	} finally {
    		if (ps != null && !ps.isClosed()) {
    			ps.close();
    		}
    	}
    }
}
